{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 简单对抗样本生成\n",
    "\n",
    "* 使用之前做过的多层感知器实验作为蓝本\n",
    "* 修改为二分类模型与论文一致\n",
    "* 寻找数据中的 3 与 7 与论文一致\n",
    "* 实验分文三个部分\n",
    " - 首先对模型进行正常训练\n",
    " - 通过梯度来产生对抗样本\n",
    " - 通过权重来产生对抗样本\n",
    "* MNIST数据集请点击[这里查看](http://yann.lecun.com/exdb/mnist/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import keras\n",
    "import numpy as np\n",
    "from keras.datasets import mnist\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Dense\n",
    "from matplotlib import pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "num_classes = 1\n",
    "epochs = 20"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# the data, split between train and test sets\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data(already_path='data/mnist.npz')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train = x_train.reshape(60000, 784)\n",
    "x_test = x_test.reshape(10000, 784)\n",
    "x_train = x_train.astype('float32')\n",
    "x_test = x_test.astype('float32')\n",
    "x_train /= 255\n",
    "x_test /= 255\n",
    "print(x_train.shape[0], 'train samples')\n",
    "print(x_test.shape[0], 'test samples')\n",
    "\n",
    "# convert class vectors to binary class matrices\n",
    "y_train = keras.utils.to_categorical(y_train, 10)\n",
    "y_test = keras.utils.to_categorical(y_test, 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据提取\n",
    "* 实验只使用数字3与7的数据，要提取出来\n",
    "* 合并数据与标签，同步打乱"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get3_7(datas, labels):\n",
    "    datas_3 = []\n",
    "    datas_7 = []\n",
    "    for index in range(labels.shape[0]):\n",
    "        condition1 = labels[index][3] == 1\n",
    "        condition2 = labels[index][7] == 1\n",
    "        if condition1:\n",
    "            datas_3.append(datas[index])\n",
    "        elif condition2:\n",
    "            datas_7.append(datas[index])\n",
    "    return np.array(datas_3), np.array(datas_7)\n",
    "\n",
    "def make_datas():\n",
    "    train_3_x, train_7_x = get3_7(x_train, y_train)\n",
    "    test_3, test_7 = get3_7(x_test, y_test)\n",
    "    train_3_y = np.zeros((train_3_x.shape[0]))\n",
    "    train_7_y = np.ones((train_7_x.shape[0]))\n",
    "    train_x = np.vstack((train_3_x, train_7_x))\n",
    "    train_y = np.hstack((train_3_y, train_7_y))\n",
    "    mix_group = list(zip(train_x, train_y))\n",
    "    np.random.shuffle(mix_group)\n",
    "    train_x, train_y = zip(*mix_group)\n",
    "    return np.array(train_x), np.array(train_y), test_3, test_7\n",
    "\n",
    "\n",
    "train_x, train_y, test_3, test_7 = make_datas()\n",
    "\n",
    "print(train_x.shape)\n",
    "print(train_y.shape)\n",
    "print(test_3.shape)\n",
    "print(test_7.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 建立模型\n",
    "* 模型会对正常数据先做一次训练，拿到成绩\n",
    "* 模型还会对被污染的测试数据进行感知测试\n",
    "* 模型还会对被污染的训练数据进行训练，看是否能达到强化模型的效果，亦或让模型更加糟糕"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "model = Sequential()\n",
    "model.add(Dense(num_classes, activation='sigmoid'))\n",
    "\n",
    "model.compile(loss='binary_crossentropy',\n",
    "              optimizer='SGD',\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "history = model.fit(train_x, train_y,\n",
    "                    batch_size=batch_size,\n",
    "                    epochs=epochs,\n",
    "                    verbose=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "score_3 = model.evaluate(test_3, np.zeros((test_3.shape[0])), verbose=0)\n",
    "score_7 = model.evaluate(test_7, np.ones((test_7.shape[0])), verbose=0)\n",
    "print('数字3准确率:', score_3[1])\n",
    "print('数字7准确率:', score_7[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 生成对抗测试样本\n",
    "### Step.1 用数字 7 生成噪声"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 计算一张数字 7 的 loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_7 = model.evaluate(test_7[0:1], np.array([1]), verbose=0)[0]\n",
    "loss_7"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 用这个 loss 计算出梯度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "delta_7 = loss_7 * test_7[0:1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 根据论文算法，计算出噪声"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eps = 0.25\n",
    "ita = eps * np.sign(delta_7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 生成一张对抗测试样本 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "adv_3 = test_3[0:1] + ita"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "adv_3[0].shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 肉眼观看生成效果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.subplot(121)\n",
    "plt.imshow(test_3[0].reshape(28,28), cmap='gray')\n",
    "plt.subplot(122)\n",
    "plt.imshow(adv_3[0].reshape(28,28), cmap='gray')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 检查被污染的照片的识别率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.predict(adv_3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.predict(test_3[0:1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 不断调整 eps 的指标，并观看实验结果吧"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Step.2 用权重生成噪声"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "_, train_7_x = get3_7(x_train, y_train)\n",
    "train_7_y = np.ones((train_7_x.shape[0]))\n",
    "\n",
    "model_new = Sequential()\n",
    "model_new.add(Dense(num_classes, activation='sigmoid'))\n",
    "\n",
    "model_new.compile(loss='binary_crossentropy',\n",
    "              optimizer='SGD',\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "history = model_new.fit(train_7_x, train_7_y,\n",
    "                    batch_size=batch_size,\n",
    "                    epochs=epochs,\n",
    "                    verbose=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "eps_new = 0.5\n",
    "w = model_new.weights[0].numpy().T\n",
    "adv_3_other = test_3[0:1] + eps_new * (np.sign(w))\n",
    "plt.imshow(adv_3_other.reshape(28,28), cmap='gray')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.predict(test_3[0:1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.predict(adv_3_other)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
